/* Release 3.10 */
/*-------------------------------------------------------------------------*
*                                                                          *
*  FSTREAM.CPP - 2.1 file stream class implementations.      		   *
*                                                                          *
*  COPYRIGHT (C) 1989..1992 Clarion Software Corporation.                  *
*  All Rights Reserved                                                     *
*                                                                          *
*--------------------------------------------------------------------------*/

#include <clib.h>
#include <fstream.hpp>

#pragma module(init_prio=>22)

_STATIC int get_mode(int h);

//**************************************************************************
//  fstream implementation
//**************************************************************************

const int filebuf::openprot = S_IREAD|S_IWRITE;


filebuf::filebuf() {

  xfd=EOF;
  opened=0;
  mode=0;
  last_seek=0;
  in_start=NULL;
  setp(base(), ebuf());
  setg(base(), ebuf(), ebuf());
}

filebuf::filebuf(int h) {

  xfd=h;
  if(h != -1) {
    opened=1;
    mode=get_mode(h);
  }
  else
    opened=0;
  last_seek=0;
  in_start=NULL;
  setp(base(), ebuf());
  setg(base(), ebuf(), ebuf());
}

filebuf::filebuf(int h, char *s, int sz) : streambuf(s, sz) {

  xfd=h;
  if(h != -1) {
    opened=1;
    mode=get_mode(h);
  }
  else
    opened=0;
  last_seek=0;
  in_start=NULL;
  if(unbuffered()) {
    setb(lahead, &lahead[1], 0);
    setp(base(), base());
    unbuffered(1);
  }
  else
    setp(base(), ebuf());
  setg(base(), ebuf(), ebuf());
}

filebuf::~filebuf() {

  if(xfd != EOF)
    close();
}

filebuf* filebuf::open(const char *path, int fmode, int permission)
  {
  unsigned om;

  if(opened)
    close();
  if(fmode&ios::binary)
    om=O_BINARY;
  else
    om=O_TEXT;
  if((fmode&(ios::in|ios::out)) == (ios::in|ios::out))
    om|=O_RDWR;
  else if(fmode&ios::in)
    om|=O_RDONLY;
  else if(fmode&ios::out)
    om|=O_WRONLY;
  if(om&(O_WRONLY|O_RDWR))
    {
    if(fmode&ios::app)
      om|=O_APPEND;
    if(fmode&ios::trunc)
      om|=O_TRUNC;
    if(!(fmode&ios::nocreate))
      om|=O_CREAT;
    }
  if((fmode&ios::noreplace) && (_exists(path)))
    xfd=EOF;
  else
    xfd=::open(path, om, permission);   // translate fmode
  if(xfd != EOF)
    {
    opened=1;
    mode =fmode;
    if(fmode&(ios::ate|ios::app))
      last_seek=lseek(xfd, 0L, SEEK_END);
    else
      last_seek=0;
    return this;
    }
  else
    {
    opened=0;
    return NULL;
    }
  }

filebuf* filebuf::close() {

  if(opened) {
    sync();
    ::close(xfd);
  }
  return this;
}

filebuf* filebuf::attach(int h) {

  if(opened)
    return 0;
  xfd=h;
  if(h != EOF) {
    opened=1;
    mode=get_mode(h);
  }
  else
    opened=0;
  return this;
}

_STATIC int get_mode(int h) {

  int mode;

  if(h >= _open_max)
     return (ios::in|ios::out);
  if(_openfd[h]&O_BINARY)
    mode=ios::binary;
  else
    mode=0;
  if(_openfd[h]&O_RDWR)
    mode |= ios::in|ios::out;
  else if(_openfd[h]&O_WRONLY)
    mode |= ios::out;
  else
    mode |= ios::in;
  if(_openfd[h]&O_APPEND)
    mode |= ios::app;
  return mode;
}

_STATIC int translate(char *buf, int num, int h) {

  int ip, op, c;

  ip=0;
  op=0;
  while(ip < num) {
    c=buf[ip];
    if(c != 0xD) {
      buf[op++]=c;
    }
    ++ip;
  }
  if((buf[ip-1] == 0xD) && (num > 1))
    ::lseek(h, -1L, SEEK_CUR);
  return op;
}

_STATIC int real_off(char *buf, int num) {

  int p, n;

  p=0;
  n=0;
  if(num > 0) {
    while(p < num) {
      if(buf[p++] == 0xA)
        n+=2;
      else
        ++n;
    }
    return n;
  }
  while(p >= num) {
    if(buf[--p] == 0xA)
      n+=2;
    else
      ++n;
  }
  return -n;
}

int filebuf::last_op() {

  int ret;

  if(pptr() > pbase())
    ret= ios::out;
  else if(in_start)
    ret= ios::in;
  else
    ret = 0;
  return ret;
}

int filebuf::overflow(int c) {

  int nw;

  if(unbuffered()) {
    if((c == EOF) || (::write(xfd, &c, sizeof(char)) == -1))
      return EOF;
    nw=1;
  }
  else {
    nw=pptr()-pbase();
    if(nw) {
      if(::write(xfd, pbase(), nw) == -1)
        return EOF;
    }
  }
  last_seek+=nw;
  setp(pbase(), epptr());
  if((c != EOF) && (!unbuffered()))
    sputc(c);
  return c;
}

int filebuf::underflow() {

  int nr;

  if(unbuffered())
    in_start=eback();
  else
    in_start=eback()+4;
  nr=ebuf()-in_start;
  last_seek=tell(xfd);
  if((nr=::_read(xfd, in_start, nr)) <= 0)
    return EOF;
  if(!(mode&ios::binary))
    nr=translate(in_start, nr, xfd);
  setg(eback(), in_start, in_start+nr);
  return (unsigned char) *gptr();
}

int filebuf::sync() {

  streampos pos;
  int lo;

  if(!opened)
    return EOF;
  lo=last_op();
  if(lo == ios::out)
     overflow();
  else if(lo == ios::in) {
    if(mode&ios::binary)
      pos = gptr()-in_start + last_seek;
    else
      pos = real_off(in_start, gptr()-in_start) + last_seek;
    last_seek = ::lseek(xfd, pos, SEEK_SET);
  }
  else
    return 0;
  setp(pbase(), epptr());
  setg(base(), ebuf(), ebuf());
  in_start=NULL;
  return 0;
}

streampos filebuf::seekoff(streamoff off, ios::seek_dir d, int ) {

  streampos p;
  ios::seek_dir dir;
  int lo;

  if(opened) {
    if((!unbuffered()) && (d == ios::cur)) {
      lo=last_op();
      if(lo == ios::out) {
        if(mode&ios::binary)
          p = pptr()-pbase();
        else
          p = real_off(pbase(), pptr()-pbase());
      }
      else if(lo == ios::in) {
        if(mode&ios::binary)
          p = gptr()-in_start;
        else
          p = real_off(eback(), gptr()-in_start);
      }
      else
        p=0;
      p += last_seek + off;
      dir=ios::beg;
    }
    else {
      p=off;
      dir=d;
    }
    sync();
    last_seek = ::lseek(xfd, p, dir);
    setp(pbase(), epptr());
    setg(base(), ebuf(), ebuf());
    return last_seek;
  }
  return (streampos) -1;
}

streambuf* filebuf::setbuf(char *s, int sz) {

  if(opened || (base() && (!unbuffered())))
    return NULL;
  setb(s, s+sz, 0);
  if(s) {
    setp(s, s+sz);
    setg(s, s+sz, s+sz);
  }
  else {
    setp(NULL, NULL);
    setg(NULL, NULL, NULL);
  }
  return this;
}

//**************************************************************************//**************************************************************************
//  fstreambase implementation
//**************************************************************************

fstreambase::fstreambase() {

}

fstreambase::fstreambase(int h) : buf(h) {

}


fstreambase::fstreambase(int h, char *s, int sz) : buf(h, s, sz) {

}

fstreambase::fstreambase(const char *path, int mode, int permission) {

  buf.open(path, mode, permission);
  if(!buf.is_open())
    setstate(failbit);
}


fstreambase::~fstreambase() {

}

void fstreambase::open(const char *path, int mode, int permission) {

  buf.open(path, mode, permission);
  if(!buf.is_open())
    state|=failbit;
}

void fstreambase::attach(int h) {

  if(buf.attach(h) == NULL)
    state|=failbit;
}

void fstreambase::close() {

  buf.close();
}

void fstreambase::setbuf(char *b, int sz) {

  buf.setbuf(b, sz);
}

//**************************************************************************//**************************************************************************
//  ifstream implementation
//**************************************************************************

ifstream::ifstream() : istream(rdbuf()) {

}

ifstream::ifstream(const char* path, int mode, int prot) : istream(rdbuf()) {

  if(rdbuf()->open(path, mode, prot) == NULL)
    setstate(badbit);
}

ifstream::ifstream(int h) : istream(rdbuf()),
                            fstreambase(h) {

}

ifstream::ifstream(int h, char* b, int sz) : istream(rdbuf()),
                                             fstreambase(h, b, sz) {
}

ifstream::~ifstream() {

}

ofstream::ofstream() : ostream(rdbuf()) {

}

ofstream::ofstream(const char* path, int mode, int prot) : ostream(rdbuf()) {

  if(rdbuf()->open(path, mode, prot) == NULL)
    setstate(badbit);
}

ofstream::ofstream(int h) : ostream(rdbuf()),
                            fstreambase(h) {

}

ofstream::ofstream(int h, char* b, int sz) : ostream(rdbuf()),
                                             fstreambase(h, b, sz) {
}

ofstream::~ofstream() {

}

fstream::fstream() : iostream(rdbuf()) {

}

fstream::fstream(const char* path, int mode, int prot) : iostream(rdbuf()) {

  if(rdbuf()->open(path, mode, prot) == NULL)
    setstate(badbit);
}

fstream::fstream(int h) : iostream(rdbuf()),
                          fstreambase(h) {

}

fstream::fstream(int h, char* b, int sz) : iostream(rdbuf()),
                                           fstreambase(h, b, sz) {
}

fstream::~fstream() {

}
